package com.service.scheduling.task;

import com.google.common.collect.Lists;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.*;
import com.service.dao.mapper.JournalAccountMapper;
import com.service.dao.mapper.MemberMapper;
import com.service.dao.model.*;
import com.service.dao.po.MemberCache;
import com.service.scheduling.dto.MemberStoreChange;
import com.service.scheduling.service.*;
import com.service.scheduling.support.MemberCacheUtils;
import com.service.scheduling.support.MemberStoreLockUtils;
import com.service.scheduling.support.OrderProductHrComboLockUtils;
import com.service.scheduling.utils.DateUtil;
import com.service.scheduling.utils.MathUtil;
import com.service.scheduling.utils.MemCacheNoTran;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * <dl>
 * <dt>back-end</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2015</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019年12月15日</dd>
 * </dl>
 *
 * @author
 */
@Slf4j
@Component
@SuppressWarnings("all")
public class HrComboBZZMiningRateTask {
    @Autowired
    private MemberCacheUtils memberCacheUtils;
    @Autowired
    private MemberMapper memberMapper;
    @Autowired
    private MemberStoreLockUtils memberStoreLockUtils;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private MemberStoreService memberStoreService;
    @Autowired
    private MemberMiningOrderService memberMiningOrderService;
    @Autowired
    private ProductHrComboService productHrComboService;
    @Autowired
    private MemberCacheService memberCacheService;
    @Autowired
    private JournalAccountMapper journalAccountMapper;
    @Autowired
    private MemberService memberService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private OrderProductHrComboService orderProductHrComboService;
    @Autowired
    private SysQuotesIpfxService sysQuotesIpfxService;
    @Autowired
    private MemCacheNoTran memCache;
    @Autowired
    private OrderProductHrComboLockUtils orderProductHrComboLockUtils;
    @Autowired
    private MemberGradeConfigService gradeConfigService;
    @Autowired
    private OrderProductHrComboPaddingService paddingService;
    @Autowired
    private CoinService coinService;

    private static BigDecimal baseNum = new BigDecimal(100L);
    private static BigDecimal directReleaseFILRate = new BigDecimal(25L);
    private static BigDecimal linearReleaseFILRate = new BigDecimal(75L);
    private static int linearReleaseEndDays = 180;

    /**
     * 定时算力
     */
    @Scheduled(cron = "0 10 0 * * ?")
    public void doCalculate() {
        log.info("BZZ算力定时任务-开始");
        String minerStatus = configService.getByConfigName(ConfigConstants.MINER_STATUS).getConfValue();
        if (MinerStatusEnum.OFF.equals(minerStatus)) {
            log.info("矿机暂停运行......");
            return;
        }
        Date currentDate = new Date();
        String pattern = "yyyy-MM-dd";
        String currentDateStr = DateFormatUtils.format(currentDate, pattern);
        Date yesterday = DateUtils.addDays(currentDate, -1);//昨天时间为当前时间的前一天
        String yesterdayStr = DateFormatUtils.format(yesterday, pattern);
        List<OrderProductHrCombo> hrComboList = orderProductHrComboService.listMiningOrder(CoinsEnum.BZZ.getCode(), yesterday);
        if (ObjectUtils.isEmpty(hrComboList)) {
            log.info("{}暂无产币BZZ算力套餐订单", currentDateStr);
            return;
        }
        SysConfig comShareConf = configService.getByConfigName(ConfigConstants.COM_FEE_SHARE_RATE);
        BigDecimal comFeeShareRate = MathUtil.div(MathUtil.parseBigDecimal(comShareConf.getConfValue()),baseNum,4);
        //申明订单结束的数组
        List<Integer> endList = Lists.newArrayList();
        Coins BZZ = coinService.getByCoinsName(CoinsEnum.BZZ.getName());
        BigDecimal outputNum = BZZ.getOutputNum();//日产出效率
        if (outputNum.compareTo(BigDecimal.ZERO) == 0) {
            log.info("{}暂无BZZ算力产出配置", currentDateStr);
            return;
        }
        hrComboList.forEach(item -> {
            //已支付且未挖矿的订单第二天凌晨开始挖矿
            if (ObjectUtils.nullSafeEquals(item.getOrderStatus(), HashRateOrderEnums.Status.payed.getCode())
                    && item.getStartTime().before(yesterday)) {
                log.info("填充算力订单<orderId:{}>还未开始", item.getId());
                return;
            }
            if (currentDate.after(item.getEndTime())) {
                log.info("填充算力订单<orderId:{}>已结束", item.getId());
                endList.add(item.getId());
                return;
            }
            if (DateUtils.isSameDay(currentDate, item.getEndTime())) {
                log.info("算力订单<orderId:{}>结算最后一天<{}>", item.getId(), currentDate);
                endList.add(item.getId());
            }
            Integer isMiningRecords = journalAccountMapper.checkByMemberAndTypeAndOrderAndSomeday(item.getMemberId(), item.getId(), currentDateStr, MemberJAType.getMiningIncomeType());
            if (isMiningRecords > 0) {
                log.info("算力订单<orderId:{}>,今日<{}>已发昨日<{}>挖矿所得", item.getId(), currentDateStr, yesterdayStr);
                return;
            }
            OrderProductHrComboDetails detail = orderProductHrComboService.getHrComboDetail(item.getId());
            BigDecimal miningNum = getHrComboRateMining(item, outputNum, item.getCustodyFee());
            BigDecimal comFeeNum = MathUtil.mul(item.getTotalHashRate(), outputNum,8).multiply(item.getCustodyFee());
            if (MathUtil.isLEZero(miningNum)) {
                log.info("算力套餐订单<orderId:{}>昨日<{}>挖矿所得为0", item.getId(), yesterdayStr);
                return;
            }
            log.info("算力套餐订单<orderId:{}>昨日<{}>挖矿所得{}", item.getId(), yesterdayStr, miningNum);
            boolean isLock = memberStoreLockUtils.lock(item.getMemberId(), CoinsEnum.BZZ.getCode());
            if (!isLock) {
                return;
            }
            try {
                memberStoreService.changeMemberStore(
                        MemberStoreChange.builder()
                                .memberId(item.getMemberId())
                                .coinsId(detail.getCoinsId())
                                .coinsName(detail.getCoinsName())
                                .jaType(MemberJAType.MINING)
                                .orderId(item.getId())
                                .orderDetailId(detail.getId())
                                .changeNum(miningNum)
                                .remark(yesterdayStr + "BZZ" + MemberJAType.MINING.getMsg())
                                .build());
            } finally {
                memberStoreLockUtils.unlock(item.getMemberId(), CoinsEnum.BZZ.getCode());
            }
            //新增公司收益记录
            JournalAccount journalAccount = JournalAccount.builder()
                    .storeId(0)
                    .storeNum(BigDecimal.ZERO)
                    .coinsId(detail.getCoinsId())
                    .coinsName(detail.getCoinsName())
                    .storeFrostNum(BigDecimal.ZERO)
                    .memberId(0)
                    .jaNum(comFeeNum)
                    .custodyFee(BigDecimal.ZERO)
                    .jaType(MemberJAType.COM_FEE_REWARD.getCode())
                    .jaRemark(MemberJAType.COM_FEE_REWARD.getMsg())
                    .orderId(item.getId())
                    .orderDetailId(detail.getId())
                    .createTime(new Date())
                    .build();
            journalAccountMapper.insert(journalAccount);
            //更新订单状态和累计收益
            OrderProductHrCombo orderProductHrCombo = new OrderProductHrCombo();
            orderProductHrCombo.setId(item.getId());
            orderProductHrCombo.setAccumulatedIncome(item.getAccumulatedIncome().add(miningNum));
            orderProductHrCombo.setOrderStatus(HashRateOrderEnums.Status.payed_effective.getCode());
            orderProductHrComboService.updateById(orderProductHrCombo);
            try{
                //用户所在的上级用户算力提成
                MemberCache memberCache = memberCacheService.getMemberCache(item.getMemberId());
                String userPath = null;
                if(!ObjectUtils.isEmpty(memberCache)){
                    userPath = memberCache.getUserPath();
                }
                if(StringUtils.isEmpty(userPath)){
                    Member member = memberService.getById(item.getMemberId());
                    if(!ObjectUtils.isEmpty(member) && !ObjectUtils.isEmpty(member.getUserPath())) {
                        userPath = member.getUserPath();
                    }else{
                        return;
                    }
                }
                List<Integer> teamIds = splitMemberId(userPath);
                if(!ObjectUtils.isEmpty(teamIds)){
                    comFeeNum = comFeeNum.multiply(comFeeShareRate);
                    rewardTopTeam(teamIds, memberCache.getMemberGrade(), comFeeNum, detail);
                }
            }catch (Exception e){
                log.info("算力订单<{}>向上级团队发放算力提成异常:by{}",item.getId(),e.getCause());
            }
        });
        //修改过期算力订单
        orderProductHrComboService.updateBZZHrComboRateOrder2Complete(endList);
        log.info("BZZ算力定时任务-结束");
    }

    /**
     * @Description: 用户上级无限返利
     * @Author: ty
     * @Date: 2021/6/16 18:45
     * @param: [teamIds, miningNum]
     * @return: void
     **/
    private void rewardTopTeam(List<Integer> teamIds, Integer currentGrade, BigDecimal miningNum, OrderProductHrComboDetails detail) {
        Map<Integer, MemberGradeConfig> gradeConfigMap = gradeConfigService.list2Map();
        Date currentDate = new Date();
        String pattern = "yyyy-MM-dd";
        Date yesterday = DateUtil.getDayOfStart(DateUtils.addDays(currentDate, -1));
        String yesterdayStr = DateFormatUtils.format(yesterday, pattern);
        if (!ObjectUtils.isEmpty(teamIds) && miningNum.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal releasedAwardRate = new BigDecimal(0);
            for (Integer memberId : teamIds) {
                MemberCache memberCache = memberCacheService.getMemberCache(memberId);
                if (ObjectUtils.isEmpty(memberCache)) {
                    log.info("用户信息未找到");
                    continue;
                }
                if(currentGrade >= GradeEnum.eleven.getValue() && memberCache.getMemberGrade() >= GradeEnum.eleven.getValue()){
                    if(memberCache.getMemberGrade() <= currentGrade){
                        log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
                        continue;
                    }
                }else if(currentGrade < GradeEnum.eleven.getValue() && memberCache.getMemberGrade() < GradeEnum.eleven.getValue()){
                    if(memberCache.getMemberGrade() <= currentGrade){
                        log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
                        continue;
                    }
                }else if(currentGrade >= GradeEnum.eleven.getValue() && memberCache.getMemberGrade() < GradeEnum.eleven.getValue()){
                    if((memberCache.getMemberGrade()+10) <= currentGrade){
                        log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
                        continue;
                    }
                }else if(currentGrade < GradeEnum.eleven.getValue() && memberCache.getMemberGrade() >= GradeEnum.eleven.getValue()){
                    if(memberCache.getMemberGrade() <= (currentGrade+10)){
                        log.info("上级用户等级{}不满足大于下级用户等级{}",memberCache.getMemberGrade(),currentGrade);
                        continue;
                    }
                }
                MemberGradeConfig gradeConfig = gradeConfigMap.get(memberCache.getMemberGrade());
                BigDecimal teamMemberRate = MathUtil.div(gradeConfig.getTeamMemberRate(), baseNum, 4);
                BigDecimal acc = teamMemberRate.subtract(releasedAwardRate);//极差减掉已发
                if(acc.compareTo(new BigDecimal(0)) <= 0){
                    continue;
                }
                log.info("会员<{}>等级<{}>发放等级极差奖励=> 释放团队速率<{}>，上一级已发速率<{}>",memberCache.getMemberId(),
                        memberCache.getMemberGrade(),acc,releasedAwardRate);
                releasedAwardRate = gradeConfig.getTeamMemberRate();//设置当前会员级别已发值
                BigDecimal rewardNum = MathUtil.mul(miningNum, acc, 8);
                boolean isLock = memberStoreLockUtils.lock(memberId, detail.getCoinsId());
                if (!isLock) {
                    continue;
                }
                if (miningNum.compareTo(BigDecimal.ZERO) <= 0) {
                    continue;
                }
                memberStoreService.changeMemberStore(
                        MemberStoreChange.builder()
                                .memberId(memberId)
                                .coinsId(detail.getCoinsId())
                                .coinsName(detail.getCoinsName())
                                .jaType(MemberJAType.RELEASE_PRODUCT_BY_TEAM)
                                .orderId(detail.getHrOrderId())
                                .orderDetailId(detail.getId())
                                .changeNum(rewardNum)
                                .remark(yesterdayStr + MemberJAType.RELEASE_PRODUCT_BY_TEAM.getMsg())
                                .build());
                memberStoreLockUtils.unlock(memberId, detail.getCoinsId());
            }
        }
    }

    /**
     * @Description: 根据用户路径获取用户团队
     * @Author: ty
     * @Date: 2021/6/16 18:35
     * @param: [userPath]
     * @return: java.util.List<java.lang.Integer>
     **/
    public List<Integer> splitMemberId(String userPath) {
        if(ObjectUtils.isEmpty(userPath)){
            return null;
        }
        String[] teamIds = userPath.split("-");
        List<Integer> memberIds = new ArrayList<>();
        for (String idStr : teamIds) {
            if (!ObjectUtils.isEmpty(idStr)) {
                memberIds.add(Integer.parseInt(idStr));
            }
        }
        //倒叙排列
        Collections.sort(memberIds, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                // 返回值为int类型，大于0表示正序，小于0表示逆序
                return o2 - o1;
            }
        });
        return memberIds;
    }

    /**
     * 更新今日订单状态
     */
    @Scheduled(cron = "0 15 0 * * ?")
//    @Scheduled(cron = "0 */5 * * * ?")
    public void updateTodayHrRate() {
        log.info("BZZ更新今日开挖算力状态-开始");
        Date currentDate = new Date();
        Date today = DateUtil.getDayOfStart(currentDate);
        List<OrderProductHrCombo> hrComboList = orderProductHrComboService.listMiningOrder(CoinsEnum.BZZ.getCode(), today);
        if (ObjectUtils.isEmpty(hrComboList)) {
            log.info("暂无算力套餐订单");
            return;
        }
        hrComboList.parallelStream().forEach(item -> {
            if (item.getOrderStatus() == HashRateOrderEnums.Status.payed.getCode() && item.getStartTime().before(currentDate)) {
                //更新今日已付款，开挖的订单状态
                OrderProductHrCombo orderProductHrCombo = new OrderProductHrCombo();
                orderProductHrCombo.setId(item.getId());
                orderProductHrCombo.setOrderStatus(HashRateOrderEnums.Status.payed_effective.getCode());
                orderProductHrComboService.updateById(orderProductHrCombo);
            }
        });
        log.info("BZZ更新今日开挖算力状态-结束");
    }

    /**
     * 得到挖矿产出量
     *
     * @param order
     * @param coins
     * @param miningRate
     * @return
     */
    public BigDecimal getHrComboRateMining(OrderProductHrCombo orderProductHrCombo, BigDecimal outputNum, BigDecimal miningRate) {
        BigDecimal totalNum = MathUtil.mul(orderProductHrCombo.getTotalHashRate(), outputNum, 8);
        return MathUtil.mul(totalNum, BigDecimal.ONE.subtract(miningRate), 8);
    }


}
